#include "win32_gl_context.h"
#include <stdlib.h> 
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <tchar.h>

#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/wglext.h>


Win32_GL_Video_Context *gWin32GLContext = NULL;
// Helper to check for extension string presence.  Adapted from:
//   http://www.opengl.org/resources/features/OGLextensions/
static bool isExtensionSupported(const char *extList, const char *extension)
 
{
 
  const char *start;
  const char *where, *terminator;
 
  /* Extension names should not have spaces. */
  where = strchr(extension, ' ');
  if ( where || *extension == '\0' )
    return false;
 
  /* It takes a bit of care to be fool-proof about parsing the
     OpenGL extensions string. Don't be fooled by sub-strings,
     etc. */
  for ( start = extList; ; ) {
    where = strstr( start, extension );
 
    if ( !where )
      break;
 
    terminator = where + strlen( extension );
 
    if ( where == start || *(where - 1) == ' ' )
      if ( *terminator == ' ' || *terminator == '\0' )
        return true;
 
    start = terminator;
  }
 
  return false;
}


int isExtensionSupported(const char *extension)
{
	const GLubyte *extensions = NULL;
	const GLubyte *start = NULL;
	GLubyte *where, *terminator;

	/* Extension names should not have spaces. */
	where = (GLubyte *) strchr(extension, ' ');
	if (where || *extension == '\0')
		return 0;
	extensions = glGetString(GL_EXTENSIONS);
	/* It takes a bit of care to be fool-proof about parsing the
	OpenGL extensions string. Don't be fooled by sub-strings,
	etc. */
	start = extensions;
	for (;;) {
		where = (GLubyte *) strstr((const char *) start, extension);
		if (!where)
			break;
		terminator = where + strlen(extension);
		if (where == start || *(where - 1) == ' ')
			if (*terminator == ' ' || *terminator == '\0')
				return 1;
		start = terminator;
	}
	return 0;
}


bool Win32_GL_Video_Context::isExtensionSupported(const char *extension)
{
	const char *where;
	const char *start = extension;
	/* Extension names should not have spaces. */
	where = strchr(extension, ' ');
	if ( where || *extension == '\0' )
		return false; // Failed because Extension to test contains spaces.

	if (m_IsGL30Context)
	{
		int i;
		/* Begin OpenGL 3.1 Extension Tests */	
	    where = strstr( start, extension );
		// Easier test using OpenGL 3.x style testing
		int NumberOfExtensions;
		glGetIntegerv(GL_NUM_EXTENSIONS, &NumberOfExtensions);
		for(i=0; i<NumberOfExtensions; i++)
		{
			//const GLubyte *one_string=glGetStringi(GL_EXTENSIONS, i);
		}
	} else {
		/* Begin OpenGL 1.x Compatible Extension Tests */
		const char *start, *terminator;
		/* It takes a bit of care to be fool-proof about parsing the
		OpenGL extensions string. Don't be fooled by sub-strings,
		etc. */
		for ( start = extension; ; ) {
			where = strstr( start, extension );

			if ( !where )
				break;

			terminator = where + strlen( extension );
			if ( where == start || *(where - 1) == ' ' ) {
				if ( *terminator == ' ' || *terminator == '\0' )
					return true; // extension found?
			}
			start = terminator;
		}	
	}
	return false;
	
}
const char* Engine_Title = "Humble Game Engine";
const char* Engine_Class = "humbleGLCore";

Win32_GL_Video_Context::Win32_GL_Video_Context()
{
	m_hDC=NULL;		// Private GDI Device Context
	m_hRC=NULL;		// Permanent Rendering Context
	m_hWnd=NULL;		// Holds Our Window Handle
	m_hInstance;		// Holds The Instance Of The Application
	void * memcpy ( void * destination, const void * source, size_t num );
	strncpy ( m_WindowName, Engine_Title, sizeof(m_WindowName));
	strncpy ( m_ClassName, Engine_Class, sizeof(m_ClassName));	
	
	m_ErrorOccurred = false;
	m_isGL_Initialized = false;
	m_IsGL30Context = false;
	m_IsGL40Context = false;
	m_Active=true;
	m_WindowPosX = 0;
	m_WindowPosY = 0;
}
Win32_GL_Video_Context::~Win32_GL_Video_Context()
{
   
}

/******************************************************************************
 * function: LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM)
 * Description:
 *     Relays Messages to the Class.
 *****************************************************************************/
LRESULT	CALLBACK OpenGL_WndProc(	HWND	hWnd,			// Handle For This Window
							UINT	uMsg,			// Message For This Window
							WPARAM	wParam,			// Additional Message Information
							LPARAM	lParam)			// Additional Message Information
{


	if (gWin32GLContext != NULL)
	{
		// handle the Windows Process via the Global method.
		return gWin32GLContext->WndProc(hWnd,uMsg,wParam,lParam);
	} else {
		// Otherwise return default.
		return DefWindowProc(hWnd,uMsg,wParam,lParam);
	}
}
/******************************************************************************
 * function: Win32_GL_Video_Context::initialize()
 * Description:
 *     Initialize X11 signals and verify GLX version.
 *****************************************************************************/
bool Win32_GL_Video_Context::initialize()
{
    bool bRetVal = true;
    DWORD dwExtStyle;
    DWORD dwWindStyle;
	WNDCLASS	ctx;
    ctx.hInstance = GetModuleHandle(NULL);

    // setup window class
    ctx.lpszClassName = TEXT(szClassName);                // Set the name of the Class
    ctx.lpfnWndProc   = (WNDPROC)OpenGL_WndProc;
    ctx.hInstance     = ctx.hInstance;                // Use this module for the module handle
    ctx.hCursor       = LoadCursor(NULL, IDC_ARROW);// Pick the default mouse cursor
    ctx.hIcon         = LoadIcon(NULL, IDI_WINLOGO);// Pick the default windows icons
    ctx.hbrBackground = NULL;                       // No Background
    ctx.lpszMenuName  = NULL;                       // No menu for this window
    ctx.style         = CS_HREDRAW | CS_OWNDC |     // set styles for this class, specifically to catch
                                    CS_VREDRAW;                 // window redraws, unique DC, and resize
    ctx.cbClsExtra    = 0;                          // Extra class memory
    ctx.cbWndExtra    = 0;                          // Extra window memory

    // Register the newly defined class
    if(!RegisterClass( &ctx))
        bRetVal = false;

    dwExtStyle  = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    dwWindStyle = WS_OVERLAPPEDWINDOW;
    ShowCursor(TRUE);
    m_WindowRect.left   = m_WindowPosX;
    m_WindowRect.right  = m_WindowPosX + m_width;
    m_WindowRect.top    = m_WindowPosX;
    m_WindowRect.bottom = m_WindowPosX + m_height;

    // Setup window width and height
    AdjustWindowRectEx(&m_WindowRect, dwWindStyle, FALSE, dwExtStyle);

    //Adjust for adornments
    int nWindowWidth  = m_WindowRect.right  - m_WindowRect.left;
    int nWindowHeight = m_WindowRect.bottom - m_WindowRect.top;

    // Create window
    m_hWnd = CreateWindowEx(dwExtStyle,     // Extended style
                              szClassName,    // class name
                              szWindowName,   // window name
                              dwWindStyle |        
                              WS_CLIPSIBLINGS | 
                              WS_CLIPCHILDREN,// window stlye
                              m_WindowPosX,   // window position, x
                              m_WindowPosY,   // window position, y
                              nWindowWidth,   // height
                              nWindowHeight,  // width
                              NULL,           // Parent window
                              NULL,           // menu
                              ctx.hInstance,    // instance
                              NULL);          // pass this to WM_CREATE

    // now that we have a window, setup the pixel format descriptor
    m_hDC = GetDC(m_hWnd);
    m_WindowClass = ctx;
    ShowWindow(m_hWnd, SW_SHOWDEFAULT);	
    return bRetVal;
}
bool Win32_GL_Video_Context::createWindow()
{
    return true;
}


bool Win32_GL_Video_Context::destroyWindow()
{
	return true;
}

void SetPerspective(GLdouble  fovy,  GLdouble  aspect,  GLdouble  zNear,  GLdouble  zFar)
{
	GLdouble PerspectiveMatrix[4][4];
	GLdouble f = (GLdouble) atan ( (double) fovy / (double)2.0f );
	
	for(int i = 0;i < 4; i++)
	{
		for(int j = 0;j < 4; j++)
			PerspectiveMatrix[0][0] = 0.0f;
	}
	
	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix
	
	// duplicate gluPerspective
	PerspectiveMatrix[0][0] = f / aspect;
	PerspectiveMatrix[1][1] = f;
	PerspectiveMatrix[2][2] = (zFar + zNear) / (zNear - zFar);
	PerspectiveMatrix[2][3] = 2* (zFar * zNear) / (2 * zNear - zFar);
	PerspectiveMatrix[3][2] = -1.0f;
	
}
bool Win32_GL_Video_Context::setResolution(int height, int width)
{
    // Todo: Add win32 calls to modify window resolution.

	// Handle OpenGL Portion of Resolution modifications.
	if (height==0)										// Prevent A Divide By Zero By
	{
		height=1;										// Making Height Equal One
	}
	glViewport(0,0,(GLsizei)width,(GLsizei)height);						// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	SetPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix	
    return true;
}
bool Win32_GL_Video_Context::setFullScreen(bool fullscreen)
{
    // Todo: Add win32 calls to toggle full screen.
    return true;
}

bool Win32_GL_Video_Context::isFullscreen()
{
    // Todo: Add randR calls.
    return true;
}

void Win32_GL_Video_Context::setWindowName(size_t s_name, char *name)
{
    // Todo: Add win32 calls to modify window name.
    return;
}


void Win32_GL_Video_Context::flipPages()
{
	// do nothing.
	SwapBuffers(m_hDC);				// Swap Buffers (Double Buffering)	
	return;
}

bool  Win32_GL_Video_Context::CreateGL2_Context()
{

	m_IsGL30Context=false;
	return true;
}

bool   Win32_GL_Video_Context::CreateGL3_Context()
{
	int attribs[] =
	{
		WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
		WGL_CONTEXT_MINOR_VERSION_ARB, 1,
		WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
		0
	};
	
	// Create Temporary Context
	HGLRC tempContext = wglCreateContext(m_hDC);
	wglMakeCurrent(m_hDC,tempContext);
	
	
	if(isExtensionSupported("WGL_ARB_create_context") == 1)
	{
		// Program
		m_wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
		m_hRC = m_wglCreateContextAttribsARB(m_hDC,0, attribs);
		if(!m_hRC) 		
		{
			// Failed to create OpenGL 3.x context.
			m_hRC = tempContext;
			return false;
		}		
		wglMakeCurrent(NULL,NULL);
		wglDeleteContext(tempContext);
		wglMakeCurrent(m_hDC, m_hRC);
		glGetIntegerv(GL_MAJOR_VERSION, &OpenGLVersion[0]);
		glGetIntegerv(GL_MINOR_VERSION, &OpenGLVersion[1]);
	} else 	{
		//It's not possible to make a GL 3.x context. Use the old style context (GL 2.1 and before)
		m_hRC = tempContext;
		return false;
	}	
	//Checking GL version
	GLVersionString = (char *)glGetString(GL_VERSION);	
	//Or better yet, use the GL3 way to get the version number 
	if (!m_hRC)
		return false;
	return true;
}
void  Win32_GL_Video_Context::PrepExtensionList()
{
	char* tmpstring = (char *)glGetString(GL_EXTENSIONS);
	char* text=(char *)malloc(strlen(tmpstring)+1);	// Allocate Memory For Our Extension String
	char* token;
	int		CurrentExtension=0;											// Local Counter Variable
	strcpy (text,tmpstring);		// Grab The Extension List, Store In Text
	token=strtok(text," ");                             // Parse 'text' For Words, Seperated By " " (spaces)
	int maxtokens = 0;
	while(token!=NULL)                              // While The Token Isn't NULL
	{
		CurrentExtension++;                                  // Increase The Counter
		if (CurrentExtension>m_TotalGLExtensions)                           // Is 'maxtokens' Less Than 'cnt'
		{
			m_TotalGLExtensions=CurrentExtension;                          // If So, Set 'maxtokens' Equal To 'cnt'
		}	
	}
}


LRESULT CALLBACK Win32_GL_Video_Context::WndProc(	HWND	hWnd,			// Handle For This Window
							UINT	uMsg,			// Message For This Window
							WPARAM	wParam,			// Additional Message Information
							LPARAM	lParam)			// Additional Message Information
{
	switch (uMsg)									// Check For Windows Messages
	{
		case WM_ACTIVATE:							// Watch For Window Activate Message
		{
			if (!HIWORD(wParam))					// Check Minimization State
			{
				m_Active=TRUE;						// Program Is Active
			}
			else
			{
				m_Active=FALSE;						// Program Is No Longer Active
			}

			return 0;								// Return To The Message Loop
		}

		case WM_SYSCOMMAND:							// Intercept System Commands
		{
			switch (wParam)							// Check System Calls
			{
				case SC_SCREENSAVE:					// Screensaver Trying To Start?
				case SC_MONITORPOWER:				// Monitor Trying To Enter Powersave?
				return 0;							// Prevent From Happening
			}
			break;									// Exit
		}

		case WM_CLOSE:								// Did We Receive A Close Message?
		{
			PostQuitMessage(0);						// Send A Quit Message
			return 0;								// Jump Back
		}

		case WM_SIZE:								// Resize The OpenGL Window
		{
			setResolution(LOWORD(lParam),HIWORD(lParam));  // LoWord=Width, HiWord=Height
			return 0;								// Jump Back
		}
	}

	// Pass All Unhandled Messages To DefWindowProc
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}